using System;
using System.Collections.Generic;
using System.Reflection;

namespace WeakEvent
{
	public sealed class FastSmartWeakEvent<T> where T : class
	{
		private struct EventEntry
		{
			public readonly FastSmartWeakEventForwarderProvider.ForwarderDelegate Forwarder;

			public readonly MethodInfo TargetMethod;

			public readonly WeakReference TargetReference;

			public EventEntry(FastSmartWeakEventForwarderProvider.ForwarderDelegate forwarder, MethodInfo targetMethod, WeakReference targetReference)
			{
				Forwarder = forwarder;
				TargetMethod = targetMethod;
				TargetReference = targetReference;
			}
		}

		private readonly List<EventEntry> eventEntries = new List<EventEntry>();

		static FastSmartWeakEvent()
		{
			if (!typeof(T).IsSubclassOf(typeof(Delegate)))
			{
				throw new ArgumentException("T must be a delegate type");
			}
			MethodInfo method = typeof(T).GetMethod("Invoke");
			if (method == null || method.GetParameters().Length != 2)
			{
				throw new ArgumentException("T must be a delegate type taking 2 parameters");
			}
			ParameterInfo parameterInfo = method.GetParameters()[0];
			if (parameterInfo.ParameterType != typeof(object))
			{
				throw new ArgumentException("The first delegate parameter must be of type 'object'");
			}
			ParameterInfo parameterInfo2 = method.GetParameters()[1];
			if (!typeof(EventArgs).IsAssignableFrom(parameterInfo2.ParameterType))
			{
				throw new ArgumentException("The second delegate parameter must be derived from type 'EventArgs'");
			}
			if (method.ReturnType != typeof(void))
			{
				throw new ArgumentException("The delegate return type must be void.");
			}
		}

		public void Add(T eh)
		{
			if (eh != null)
			{
				Delegate @delegate = (Delegate)(object)eh;
				if (eventEntries.Count == eventEntries.Capacity)
				{
					RemoveDeadEntries();
				}
				MethodInfo method = @delegate.Method;
				object target = @delegate.Target;
				WeakReference targetReference = ((target != null) ? new WeakReference(target) : null);
				eventEntries.Add(new EventEntry(FastSmartWeakEventForwarderProvider.GetForwarder(method), method, targetReference));
			}
		}

		private void RemoveDeadEntries()
		{
			eventEntries.RemoveAll((EventEntry ee) => ee.TargetReference != null && !ee.TargetReference.IsAlive);
		}

		public void Remove(T eh)
		{
			if (eh == null)
			{
				return;
			}
			Delegate @delegate = (Delegate)(object)eh;
			object target = @delegate.Target;
			MethodInfo method = @delegate.Method;
			for (int num = eventEntries.Count - 1; num >= 0; num--)
			{
				EventEntry eventEntry = eventEntries[num];
				if (eventEntry.TargetReference != null)
				{
					object target2 = eventEntry.TargetReference.Target;
					if (target2 == null)
					{
						eventEntries.RemoveAt(num);
					}
					else if (target2 == target && eventEntry.TargetMethod == method)
					{
						eventEntries.RemoveAt(num);
						break;
					}
				}
				else if (target == null && eventEntry.TargetMethod == method)
				{
					eventEntries.RemoveAt(num);
					break;
				}
			}
		}

		public void Raise(object sender, EventArgs e)
		{
			bool flag = false;
			EventEntry[] array = eventEntries.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				EventEntry eventEntry = array[i];
				flag |= eventEntry.Forwarder(eventEntry.TargetReference, sender, e);
			}
			if (flag)
			{
				RemoveDeadEntries();
			}
		}
	}
}
